﻿using System;
using Aeon.Framework;
using System.Windows.Forms;
using Aeon.Framework.Objects;
using Aeon.Testing.Properties;
using Aeon.Framework.Objects.Models;

namespace Aeon.Testing.Models
{

public partial class WindowDemoMdl : Form
    {
    // --- Fields:
    
    private Int32 frames;       // Frame accumulator.
    private Text display;       // Frame display.
    private Timer fps;          // Frame counter.

    private Timer timer;        // Frame manager.
    private World scene;        // Scene manager.

    private Position memory;    // Screen click memory.
    private Single orbit;       // Moon orbit memory.

    #region Content
    private static String path = Application.StartupPath+@"\Demos\Content\";
    
    private FontType styleB = new FontType(path+"Bajoran.ttf",14,AeonColors.Red);

    private String starS = path+"starSphere.jpg";
    private String greenP = path+"wetlands.jpg";
    private String magmaP = path+"magma.jpg";
    private String sunP = path+"sun.jpg";
    private String cloudCover = path+"clouds.png";

    private String modelTransport = path+"MediumTransport.3ds";
    #endregion

    // --- Constructor:

    public WindowDemoMdl()
        {
        InitializeComponent();
        Icon = Resources.AeonIco;

        // Prepare GL:
        
        Display.InitializeContexts();
        Display.MouseUp += PanHandler;
        Display.Paint += RenderHandler;
        Display.MouseDown += ClickHandler;
        Display.MouseWheel += ZoomHandler;
        Display.PreviewKeyDown += KeyHandler;
                
        // Build World:
        
        WorldManager.SetModules(typeof(AeonTextureManager),typeof(AeonFontManager),typeof(MockAudioManager),typeof(AeonInputManager),typeof(MockPhysicsManager));
        scene = WorldManager.CreateWorld("Test World");

        initialize();

        // Prepare Inputs:

        InputContext icMain = scene.InputManager.GetInputs("main");
        scene.InputManager.MakeActive("main");

        icMain.InsertHook("quit",inputQuit);
        icMain.InsertHook("zoomMore",inputZoomIn);
        icMain.InsertHook("zoomLess",inputZoomOut);
        icMain.InsertHook("rotateUp",inputRotateUp);
        icMain.InsertHook("rotateDown",inputRotateDown);
        icMain.InsertHook("rotateLeft",inputRotateLeft);
        icMain.InsertHook("rotateRight",inputRotateRight);
        icMain.InsertHook("moveDown",inputMoveDown);
        icMain.InsertHook("moveUp",inputMoveUp);
        icMain.InsertHook("moveLeft",inputMoveLeft);
        icMain.InsertHook("moveRight",inputMoveRight);
        icMain.InsertHook("centerCamera",inputCenterCamera);
                
        // Start World:
        
        timer = new Timer();
        timer.Interval = 55;
        timer.Tick += Timer_Tick;

        timer.Start();

        frames = 0;
        fps = new Timer();
        fps.Interval = 1000;
        fps.Tick += Fps_Tick;

        fps.Start();
        }

    // --- Internal Methods:

    /// <summary>
    /// Creates graphical enviroment.
    /// 
    /// </summary>
    private void initialize()
        {
        // Camera:
        
        scene.SetCamera(Display.Height,Display.Width);
        scene.CameraMoveTo(new Position(0,0,2000));
        scene.CameraTurnTo(new Framework.Orientation(0,15,0));
        
        // Lights:
        
        AeonColor ambU = new AeonColor(0.2f,0.2f,0.2f,1);
        AeonColor difU = new AeonColor(0.8f,0.8f,0.8f,1);
        AeonColor spcU = new AeonColor(0.5f,0.5f,0.5f,1);
        Position posU = new Position(0,300,-3000);

        AeonLight lightA = new AeonLight("Light A",posU,ambU,difU,spcU);
        scene.Add(lightA);
        
        // Environment:

        Skydome domeUp = new Skydome("StardomeU",Position.Origin,new Framework.Orientation(60,0,0),11000,10,AeonMaterials.Default);
        domeUp.Material.Color = AeonColors.White;
        domeUp.Material.Texture.Name = starS;
        scene.Add(domeUp);

        Skydome domeDo = new Skydome("StardomeD",Position.Origin,new Framework.Orientation(60,-180,0),11000,10,AeonMaterials.Default);
        domeDo.Material.Color = AeonColors.White;
        domeDo.Material.Texture.Name = starS;
        scene.Add(domeDo);

        Sphere sun = new Sphere("Sun",posU,Framework.Orientation.Origin,50);
        sun.Material.Texture.Name = sunP;
        scene.Add(sun);

        Sphere planet = new Sphere("Planet",Position.Origin,Framework.Orientation.Origin,90);
        planet.Material.Texture.Name = greenP;
        scene.Add(planet);

        Sphere clouds = new Sphere("Clouds",Position.Origin,Framework.Orientation.Origin,92);
        clouds.Material.Texture.Name = cloudCover;
        scene.Add(clouds);

        Sphere moon = new Sphere("Moon",new Position(100,100,100),Framework.Orientation.Origin,30);
        moon.Material.Texture.Name = magmaP;
        scene.Add(moon);

        // Models:

        //Model<Parser3DS> trans = new Model<Parser3DS>("Transport",new Position(200,200,200),Framework.Orientation.Origin,AeonMaterials.Default,modelTransport);
        //scene.Add(trans);

        // Text:

        display = new Text("Fps", new Position(0.7f,0.9f),scene.FontManager.GetFont(styleB),"FPS:");
        scene.Add(display);
        }

    /// <summary>
    /// Executes all entity updates before rerendering.
    /// 
    /// </summary>    
    private void update()
        {
        // Rotate planets on their axis
        scene.TurnBy("Planet",new Framework.Orientation(0,0,0.2f));
        scene.TurnBy("Clouds",new Framework.Orientation(0.1f,0,0.3f));
        scene.TurnBy("Moon",new Framework.Orientation(0.2f,0,0.2f));

        // Orbit moon centered on (0,0,0)
        orbit += 0.001f;
        if (orbit>360) orbit -= 360;
        Single X = (Single)Math.Sin(orbit)*245;
        Single Z = (Single)Math.Cos(orbit)*245;

        Position update = new Position(X,0,Z);
        scene.MoveTo("Moon",update);
        }

    /// <summary>
    /// Stops resource use.
    /// 
    /// </summary>
    private void free()
        {
        timer.Stop();
        scene.DestroyScene();
        
        Display.DestroyContexts();
        }

    /// <summary>
    /// Closes window.
    /// 
    /// </summary>
    private void exit()
        {
        
        this.Close();
        }

    // --- Component Handlers:

    /// <summary>
    /// Executes at every tick of the timer.
    /// Reports number of frames that second.
    /// 
    /// </summary>
    private void Fps_Tick(Object sender, EventArgs e)
        {
        display.Write = "FPS: " + frames.ToString();

        frames = 0;
        }

    /// <summary>
    /// Executes at every tick of the timer.
    /// Updates scene information and redraws.
    /// 
    /// </summary>
    private void Timer_Tick(Object sender, EventArgs e)
        {
        frames++;

        update();
        Display.Invalidate();
        }
    
    /// <summary>
    /// Captures all close events.
    /// Makes sure resources are released.
    /// 
    /// </summary>    
    private void Form_Close(Object sender,FormClosingEventArgs e)
        {
        
        free();
        }

    // --- Input Handlers:

    private void inputQuit() { exit(); }
    private void inputZoomIn() { scene.CameraMoveBy(new VectorC(0,0,-30)); }
    private void inputZoomOut() { scene.CameraMoveBy(new VectorC(0,0,30)); }
    private void inputRotateUp() { scene.CameraTurnBy(new Framework.Orientation(-15,0,0)); }
    private void inputRotateDown() { scene.CameraTurnBy(new Framework.Orientation(15,0,0)); }
    private void inputRotateLeft() { scene.CameraTurnBy(new Framework.Orientation(0,-15,0)); }
    private void inputRotateRight() { scene.CameraTurnBy(new Framework.Orientation(0,15,0)); }
    private void inputMoveDown() { scene.CameraMoveBy(new VectorC(0,-20,0)); }
    private void inputMoveUp() { scene.CameraMoveBy(new VectorC(0,20,0)); }
    private void inputMoveLeft() { scene.CameraMoveBy(new VectorC(-20,0,0)); }
    private void inputMoveRight() {  scene.CameraMoveBy(new VectorC(20,0,0)); }
    private void inputCenterCamera() { scene.CameraMoveTo(new Position(0,0,2000)); scene.CameraTurnTo(new Framework.Orientation(0,15,0)); }

    // --- OpenGL Handlers:

    /// <summary>
    /// Handler for the OnPaint event.
    /// 
    /// </summary>    
    public void RenderHandler(Object obj, PaintEventArgs arg)
        {
        
        scene.RenderScene();
        }

    /// <summary>
    /// Handler for the MouseWheel event.
    /// Mouse wheel controls zoom.
    /// 
    /// </summary>
    public void ZoomHandler(Object obj, MouseEventArgs arg)
        {

        scene.CameraMoveBy(new VectorC(0,0,arg.Delta/1.3f));
        }

    /// <summary>
    /// Handler for MouseDown event.
    /// Click sets position memory for the PanHandler.
    /// 
    /// </summary>
    public void ClickHandler(Object obj, MouseEventArgs arg)
        {
        switch (arg.Button)
            {
            case MouseButtons.Right: memory.Change(new Position(arg.X,arg.Y)); break;
            case MouseButtons.Middle: memory.Change(new Position(arg.X,arg.Y)); break;
            }
        }

    /// <summary>
    /// Handler for MouseUp event.
    /// Button drag delta controls translation and rotation pan.
    /// 
    /// </summary>    
    public void PanHandler(Object obj, MouseEventArgs arg)
        {
        int posPrecision = 3;
        int rotPrecision = 5;

        switch (arg.Button)
            {
            case MouseButtons.Right:
                {
                float x = -((arg.X-Display.Height/2)-(memory.xPos-Display.Height/2))/posPrecision;
                float y = ((arg.Y-Display.Width/2)-(memory.yPos-Display.Width/2))/posPrecision;

                scene.CameraMoveBy(new VectorC(x,y,0));
                break;
                }

            case MouseButtons.Middle:
                {
                float x = -((arg.Y-Display.Height/2)-(memory.yPos-Display.Height/2))/rotPrecision;
                float z = -((arg.X-Display.Width/2)-(memory.xPos-Display.Width/2))/rotPrecision;

                scene.CameraTurnBy(new Framework.Orientation(x,0,z));
                break;
                }
            }
        }

    /// <summary>
    /// Handler for KeyPress event.
    /// Key redundancy for mouse and button actions.
    /// 
    /// </summary>
    private void KeyHandler(Object sender, PreviewKeyDownEventArgs arg)
        {
        // Prevents arrows from losing focus.
        arg.IsInputKey = true;

        String input = String.Empty;

        switch (arg.KeyCode)
            {
            case Keys.Escape: input = "quit"; break;
            
            case Keys.PageUp: input = "zoomMore"; break;
            case Keys.PageDown: input = "zoomLess"; break;
            case Keys.Up: input = "rotateUp"; break;
            case Keys.Down: input = "rotateDown"; break;
            case Keys.Left: input = "rotateLeft"; break; 
            case Keys.Right: input = "rotateRight"; break;
            case Keys.NumPad2: input = "moveDown"; break;
            case Keys.NumPad8: input = "moveUp"; break;
            case Keys.NumPad4: input = "moveLeft"; break; 
            case Keys.NumPad6: input = "moveRight"; break; 
            case Keys.NumPad5: input = "centerCamera"; break;
            }

        scene.InputManager.Notify(input);
        }

    }


}

